#include "varTable.h"

#define defaultTableSize 41

typedef struct {
	char *name;
	double value;
	int error;
	int reserved;
} tableEntry;

int varTableErr = 0;
tableEntry *table;
int size = defaultTableSize;
int num = 0;

int setUpTable() {

	table = malloc(sizeof(tableEntry) * size);
	if (table == NULL)
		return 0;

	for (int i = 0; i < size; i++) {
		table[i].name = NULL;
		table[i].value = 0;
		table[i].error = 0;
	}

	return 1;
}

int lookup(char *name) {
	
	int index = hash(name);

	while (table[index].name != NULL) {
		if (strcmp(name, table[index].name) == 0)
			return index;
		
		if (index < size)
			index++;
		else
			index = 0;
	}

	return -1; // -1 mean no entry exists
}

int addTableValue(char *name, double value) {
	varTableErr = 0;	
	int index = lookup(name);

	if (index != -1) {
		table[index].value = value;
		if (table[index].reserved)
			varTableErr = TBL_ERR_RESERVED;
		return value;
	}

	index = hash(name);

	while (table[index].name != NULL) {
		index++;
	}

	table[index].name = malloc(sizeof(char) * (strlen(name) + 1));
	strcpy(table[index].name, name);
	table[index].value = value;
	table[index].reserved = 0;

	return value;
}

int addTableValueReserved(char *name, double value) {
	varTableErr = 0;	
	int index = lookup(name);

	if (index != -1) {
		table[index].value = value;
		return value;
	}

	index = hash(name);

	while (table[index].name != NULL) {
		index++;
	}

	table[index].name = malloc(sizeof(char) * (strlen(name) + 1));
	strcpy(table[index].name, name);
	table[index].value = value;
	table[index].reserved = 1;

	return value;
}

int addTableError(char *name, int error) {
	
	int index = lookup(name);
	
	if (index != -1) {
		table[index].error = error;
		return 1;
	}

	index = hash(name);

	while (table[index].name != NULL) {
		index++;
	}

	table[index].name = malloc(sizeof(char) * (strlen(name) + 1));
	strcpy(table[index].name, name);
	table[index].error = error;

	return 1;
}

double getTableValue(char *name) {
	varTableErr = 0;
	int index = lookup(name);

	if (index != -1) {
		if (table[index].error)
			varTableErr = TBL_ERR_NO_VALUE;
		return table[index].value;
	}
	
	varTableErr = TBL_ERR_UNDEFINED;
	return 0;
}

// implementation of djb2 hash algorithm
int hash(char *name) {
	unsigned long hash = 5381;

	int c;
	while ((c = *name++))
		hash = ((hash << 5) + hash) + c; /* hash * 33 + c */

	return hash % size;
}

int freeTable() {
	
	for (int i = 0; i < size; i++)
		if (table[i].name != NULL)
			free(table[i].name);

	free(table);

	return 1;
}


int getTableError() {
	int temp = varTableErr;
	varTableErr = TBL_ERR_NO_ERR;
	return temp;
}

void printTableErrors(void (*error)(const char *), void (*warning)(const char *), char *variable) {
	char *errorStr = malloc(sizeof(char)*(strlen(variable) + 50));

	switch (varTableErr) {
		case TBL_ERR_NO_VALUE:
			sprintf(errorStr, "invalid variable %s", variable);
			(*error)(errorStr);
			break;
		case TBL_ERR_UNDEFINED:
			sprintf(errorStr, "undefined variable %s", variable);
			(*error)(errorStr);
			break;
		case TBL_ERR_RESERVED:
			sprintf(errorStr, "reserved variable %s", variable);
			(*warning)(errorStr);
			break;
		case TBL_ERR_NO_ERR:
			break;
		default:
			sprintf(errorStr, "unknown error on variable %s", variable);
			(*error)(errorStr);
	}
	free(errorStr);
}

char *getTableErrorString(int err) {

	switch (err) {
		case TBL_ERR_NO_VALUE:
			return "invalid variable";
		case TBL_ERR_UNDEFINED:
			return "undefined variable";
		case TBL_ERR_RESERVED:
			return "";
	}
	return "Unknown Error";

}

